/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "ReplicationRestoreJob.h"
#include "boost/algorithm/string.hpp"
#include "client/ClientInvoke.h"
#include "utils/PluginUtilities.h"
using namespace AppProtect;
using namespace Module;

namespace {
    constexpr auto MODULE = "ReplicationRestoreJob";
    constexpr int MAX_ROLLBACK_RATE = 100;
    constexpr uint16_t SNAPSHOT_REVERT_FINISHED = 30;
    constexpr uint16_t SCAN_TIME_INTERVAL_SECEND = 5;
    constexpr float PROGRESS_OVERALL = 100.0;
    constexpr int32_t PROGRESS_HUNDRED = 100;
    constexpr int32_t PROGRESS_TEN = 10;
    constexpr int32_t PROGRESS_TWENTY = 20;
    constexpr int32_t SYNCHRONIZE_FINISHED = 100;
    constexpr int SCAN_RETRY_TIMES = 720; // 每隔5s扫描一次，超时时间1小时
    constexpr int RETRY_TIMES = 3;
    constexpr int MONITOR_ROLLBACK_TIMES = 6;
}

ReplicationRestoreJob::ReplicationRestoreJob()
{
};

int ReplicationRestoreJob::CheckBackupJobType()
{
    return MP_SUCCESS;
}

int ReplicationRestoreJob::AllowBackupInLocalNode()
{
    SetJobToFinish();
    return MP_SUCCESS;
}

int ReplicationRestoreJob::PostJobInner()
{
    HCP_Log(INFO, MODULE) << "Start to execute post job, main task id is " << m_parentJobId
                          << ", sub task id is" << m_subJobInfo->subJobId << HCPENDLOG;
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    if (!InitDeviceInfo()) {
        HCP_Log(ERR, MODULE) << "Init device info fail" << HCPENDLOG;
        RemoveCertification(m_restorePara->targetEnv);
        AddLogDetail(logDetail, "nas_plugin_init_env_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return MP_FAILED;
        }
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::FAILED);
        return MP_FAILED;
    }

    RestoreLocation restoreLocation = RestoreLocation::ORIGINAL_AND_DIFFFILESYTEM;
    if (!ConfirmRestoreLocation(restoreLocation)) {
        RemoveCertification(m_restorePara->targetEnv);
        AddLogDetail(logDetail, "nas_plugin_init_env_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return MP_FAILED;
        }
        AddErrCode(logDetail, m_remoteDeviceController->GetErrorCode());
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::FAILED);
        return MP_FAILED;
    }
    if (!DeletePairWhenRestore(restoreLocation)) {
        RemoveCertification(m_restorePara->targetEnv);
        return MP_FAILED;
    }
    if (restoreLocation != RestoreLocation::NEWPOS_AND_DIFFFILESYTEM) {
        CleanTask(m_jobResult);
    }
    // 删除落盘的证书信息
    if (!RemoveCertification(m_restorePara->targetEnv)) {
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::FAILED);
        return MP_FAILED;
    }
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, PROGRESS_HUNDRED, 0, SubJobStatus::COMPLETED);
    HCP_Log(INFO, MODULE) << "Finish to execute post job, main task id is " << m_parentJobId << HCPENDLOG;
    return MP_SUCCESS;
}

bool ReplicationRestoreJob::UpdateBasicDeviceInfo(ControlDeviceInfo &basicDeviceInfo)
{
    basicDeviceInfo.url = ""; // 使用安全IP登录，不需要IP和端口号
    basicDeviceInfo.compress = false;
    basicDeviceInfo.dedup = false;
    basicDeviceInfo.poolId = 0;
    basicDeviceInfo.deviceName = m_restorePara->copies[0].snapshots[0].parentName; // DME创建的文件系统名称
    HCP_Log(INFO, MODULE) << "Basic fileSystem Name: " << WIPE_SENSITIVE(basicDeviceInfo.deviceName) << HCPENDLOG;
    for (std::size_t index = 0; index < m_restorePara->copies[0].repositories.size(); index++) {
        StorageRepository storage = m_restorePara->copies[0].repositories[index];
        if (storage.repositoryType == RepositoryDataType::type::DATA_REPOSITORY) {
            basicDeviceInfo.userName = storage.extendAuth.authkey;
            basicDeviceInfo.password = storage.extendAuth.authPwd;
            HCP_Log(INFO, MODULE) << "basicDeviceInfo.userName: "
                << WIPE_SENSITIVE(basicDeviceInfo.userName) << HCPENDLOG;

            std::vector<std::string> mgrIpList;
            try {
                boost::split(mgrIpList, storage.endpoint.ip, boost::is_any_of(","), boost::token_compress_off);
            } catch (std::exception &ex) {
                HCP_Log(ERR, MODULE) << "storage.endpoint.ip:[" << storage.endpoint.ip
                    << "] format is wrong" << HCPENDLOG;
                return false;
            }

            basicDeviceInfo.port = "8088";
            std::string innerDoradoIp;
            if (GetContainerDoradoIp(innerDoradoIp) == MP_SUCCESS && (!innerDoradoIp.empty())) {
                mgrIpList.push_back(innerDoradoIp);
                HCP_Log(DEBUG, MODULE) << "Dorado ip in container is " << innerDoradoIp << HCPENDLOG;
            }
            HCP_Log(INFO, MODULE) << "storage Device IP List " << storage.endpoint.ip << HCPENDLOG;
            if (!UpdateDeviceAvilableIP(basicDeviceInfo, mgrIpList)) {
                return false;
            }
            std::shared_ptr<DoradoNas> deviceControl = std::make_shared<DoradoNas>(basicDeviceInfo, false);
            DeviceDetails info;
            deviceControl->QueryFileSystem(info);
            m_basicFSID = deviceControl->GetFileSystemId();
            return true;
        }
    }
    return false;
}

bool ReplicationRestoreJob::UpdateProductDeviceInfo(ControlDeviceInfo &productDeviceInfo)
{
    productDeviceInfo.userName = m_restorePara->targetEnv.auth.authkey;
    productDeviceInfo.password = m_restorePara->targetEnv.auth.authPwd;
    HCP_Log(INFO, MODULE) << "productDeviceInfo.userName: " << WIPE_SENSITIVE(productDeviceInfo.userName) << HCPENDLOG;
    productDeviceInfo.port = std::to_string(m_restorePara->targetEnv.port);
    productDeviceInfo.deviceName = m_restorePara->targetObject.name;

    ReplicationTargetObjectExtendInfo extendInfo {};
    if (!JsonHelper::JsonStringToStruct(m_restorePara->targetObject.extendInfo, extendInfo)) {
        HCP_Log(ERR, MODULE) << "ReplicationTargetObjectExtendInfo is invaild." << HCPENDLOG;
        return false;
    }
    m_productFSID = extendInfo.fileSystemId;
    HCP_Log(INFO, MODULE) << "Product device ip list: " << m_restorePara->targetEnv.endpoint << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "m_restorePara->targetObject.extendInfo: " <<
        WIPE_SENSITIVE(m_restorePara->targetObject.extendInfo) << HCPENDLOG;
    std::vector<std::string> productMgrIpList;
    if (!GetMgrIpFromExt(productMgrIpList, m_restorePara->targetEnv.extendInfo) ||
        !GetRepIpFromExt(m_restorePara->targetEnv.extendInfo)) {
        ERRLOG("GetMgrIpFromExt failed");
        return false;
    }

    if (!UpdateDeviceAvilableIP(productDeviceInfo, productMgrIpList)) {
        return false;
    }
    // 检查是否校验CA证书，并对证书落盘
    if (!CertVerifyMgr(m_restorePara->targetEnv, productDeviceInfo)) {
        return false;
    }
    return true;
}

bool ReplicationRestoreJob::InitializeTask()
{
    HCP_Log(INFO, MODULE) << "Begin to initialize homo_dpa_task" << HCPENDLOG;
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    SubJobStatus::type status = SubJobStatus::FAILED;
    if (!InitDeviceInfo()) {
        HCP_Log(ERR, MODULE) << "Init device info fail" << HCPENDLOG;
        AddLogDetail(logDetail, "plugin_execute_prerequisit_task_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, HomoErrorCode::BACKUP_PARAMETER_CHECK_FAILED_ERROR_CODE); // 初始化环境参数失败
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, status);
        return false;
    }
    m_nasReplicationServiceObj = NasRepServiceObjOp::NasReplicationService::GetReplicationInstance(
        m_productFSID, m_basicFSID, m_qos);
    m_nasReplicationServiceObj->SetTaskID(m_parentJobId);
    if (!m_selectedRepNameList.empty()) {
        std::vector<std::string> repPortNameList(m_selectedRepNameList.begin(), m_selectedRepNameList.end());
        m_nasReplicationServiceObj->SetProduckIscsiList(repPortNameList);
    }
    if ((m_remoteDeviceController->GetESN(m_productEsn) != MP_SUCCESS) ||
        (m_nasReplicationServiceObj->PrepareRemoteReplicatonPair(m_localDeviceController, m_remoteDeviceController)
        != NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceNormal)) {
        HCP_Log(ERR, MODULE) << "Prepare remotereplicatonpair fail" << HCPENDLOG;
        AddLogDetail(logDetail, "plugin_execute_prerequisit_task_fail_label", JobLogLevel::TASK_LOG_ERROR,
            m_productEsn, m_productFSID, m_basicFSID);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        int64_t errCode = (m_localDeviceController->GetErrorCode() != MP_SUCCESS)
            ? m_localDeviceController->GetErrorCode() : m_remoteDeviceController->GetErrorCode();
        AddErrCode(logDetail, errCode);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, status);
        return false;
    }
    status = SubJobStatus::RUNNING;
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, status);
    HCP_Log(INFO, MODULE) << "Initialize homo_dpa_task success" << HCPENDLOG;
    return true;
}

bool ReplicationRestoreJob::InitDeviceInfo()
{
    ControlDeviceInfo basicDeviceInfo;
    ControlDeviceInfo productDeviceInfo;
    HCP_Log(INFO, MODULE) << "Enter InitDeviceInfo" << HCPENDLOG;
    if (!GetJobInfoBody()) {
        HCP_Log(ERR, MODULE) << "Get Job Info failed" << HCPENDLOG;
        return false;
    }
    if (m_qos <= 0) {
        m_qos = 1024; // 未配置Qos策略，默认按1024M执行
    }
    ApplicationEnvExtent applicationEnvExtentInfo {};
    if (!JsonHelper::JsonStringToStruct(m_restorePara->targetEnv.extendInfo, applicationEnvExtentInfo)) {
        HCP_Log(ERR, MODULE) << "ApplicationEnvExtentInfo is invaild." << HCPENDLOG;
    }
    if (std::stoi(applicationEnvExtentInfo.verifyStatus) == MP_TRUE) {
        if (callCheckCertThumbPrint(m_restorePara->targetEnv) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "CheckCert thumbprint failed" << HCPENDLOG;
            return false;
        }
    }
    if (!UpdateBasicDeviceInfo(basicDeviceInfo)) {
        return false;
    }
    m_snapshotName = m_restorePara->copies[0].name;
    HCP_Log(INFO, MODULE) << "Snapshot name: " << WIPE_SENSITIVE(m_snapshotName) << HCPENDLOG;
    m_localDeviceController = std::make_unique<DoradoNas>(basicDeviceInfo, m_basicFSID, false);
    if (m_localDeviceController->QuerySnapshot(m_snapshotName, m_snapshotId) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Query snapShot " << WIPE_SENSITIVE(m_snapshotName)
            << " in localstorage failed." << HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "Snapshot id: " << m_snapshotId << HCPENDLOG;
    m_productFSID = "";
    if (!UpdateProductDeviceInfo(productDeviceInfo)) {
        return false;
    }
    m_remoteDeviceController = std::make_unique<DoradoNas>(productDeviceInfo, m_productFSID, false);
    m_remoteDeviceController->SetRetryAttr(RETRY_TIMES);  // 设置重试次数(3次)，间隔默认10秒
    m_isBackUpFlow = false; // 恢复
    return true;
}

int ReplicationRestoreJob::callCheckCertThumbPrint(const AppProtect::ApplicationEnvironment& targetEnv)
{
    EnvAuthExtendInfo authExtendInfo {};
    if (!JsonHelper::JsonStringToStruct(targetEnv.auth.extendInfo, authExtendInfo)) {
        HCP_Log(ERR, MODULE) << "NasAuthExtendInfo is invaild." << HCPENDLOG;
    }
    const std::string thumbPrint = authExtendInfo.fingerPrint;
    const std::string endpoint = targetEnv.endpoint;
    const int32_t port = targetEnv.port;
    HCP_Log(INFO, MODULE) << "CheckCertThumbprint param"
        << ", endpoint: " << endpoint
        << ", port: " << port
        << ", thumbprint: " << WIPE_SENSITIVE(thumbPrint) << HCPENDLOG;
    ActionResult ret;
    SecurityService::CheckCertThumbPrint(ret, endpoint, port, thumbPrint);
    if (ret.code != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "CheckCertThumbprint failed"
        << ", endpoint: " << endpoint
        << ", port: " << port
        << ", thumbprint: " << WIPE_SENSITIVE(thumbPrint) << HCPENDLOG;
        return MP_FAILED;
    }
    return MP_SUCCESS;
}

bool ReplicationRestoreJob::CheckAndCreatePair()
{
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    ActionResult result;
    if (m_nasReplicationServiceObj->IsExistPairValid()) {
        HCP_Log(INFO, MODULE) << "Existed a valid replication pair" << HCPENDLOG;
        AddLogDetail(logDetail, "", JobLogLevel::TASK_LOG_INFO, m_pairID);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
        return true;
    }
    HCP_Log(INFO, MODULE) << "Pair " << m_pairID << " is invalid, Create a new pair" << HCPENDLOG;
    int remoteResId = std::atoi(m_productFSID.c_str());
    int localResId = std::atoi(m_basicFSID.c_str());
    std::string remoteDoradoESN;
    m_remoteDeviceController->GetESN(remoteDoradoESN);
    std::string remoteDevId;
    if (m_localDeviceController->BatchQueryRemoteDevice(remoteDoradoESN, remoteDevId) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Query remote device fail" << HCPENDLOG;
        AddLogDetail(logDetail, "plugin_execute_prerequisit_task_fail_label", JobLogLevel::TASK_LOG_ERROR,
            remoteDoradoESN);
        AddErrCode(logDetail, HomoErrorCode::REMOTE_DEVICE_NOTEXIST_ERROR_CODE);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0,
            SubJobStatus::RUNNING);
        return false;
    }
    if (!m_nasReplicationServiceObj->CreateReplicationPair(m_localDeviceController, localResId, remoteDevId,
        remoteResId, m_pairID)) {
        HCP_Log(ERR, MODULE) << "Create a new pair fail" << HCPENDLOG;
        AddLogDetail(logDetail, "plugin_execute_prerequisit_task_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, m_localDeviceController->GetErrorCode());
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0,
            SubJobStatus::FAILED);
        return false;
    }
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0,
        SubJobStatus::RUNNING);
    HCP_Log(INFO, MODULE) << "Create a new pair " << m_pairID << HCPENDLOG;
    return true;
}

bool ReplicationRestoreJob::PreStartTask()
{
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    if (!InitDeviceInfo()) {
        AddLogDetail(logDetail, "nas_plugin_restore_filesystem_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return false;
    }
    m_nasReplicationServiceObj = NasRepServiceObjOp::NasReplicationService::GetReplicationInstance(
        m_productFSID, m_basicFSID, m_qos);
    m_nasReplicationServiceObj->SetTaskID(m_parentJobId);

    m_remoteDeviceController->GetESN(m_productEsn);
    if (!m_nasReplicationServiceObj->GetPairInfoInProduct(
        m_remoteDeviceController, m_localDeviceController, m_pairID)) {
        AddLogDetail(logDetail, "nas_plugin_restore_filesystem_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, HomoErrorCode::REPLICATION_PAIR_NOTEXIST_ERROR_CODE);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return false;
    }

    if (!SplitReplicationPairForRevertSnapshot()) { // 防止进程重启， pair 仍然存在
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return false;
    }

    if (!RevertSnapShotInStorage()) {
        return false;
    }

    return true;
}

bool ReplicationRestoreJob::SplitReplicationPairForRevertSnapshot()
{
    HCP_Log(INFO, MODULE) << "Enter SplitReplicationPairForRevertSnapshot" << HCPENDLOG;

    int64_t errorCode = WaitingOtherReplicationPairComplete(m_localDeviceController, m_remoteDeviceController);
    if (errorCode != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "WaitingOtherReplicationPairComplete fail " << m_pairID << HCPENDLOG;
        return false;
    }

    if (!m_nasReplicationServiceObj->SplitReplicationPair(m_localDeviceController, m_pairID)) {
        HCP_Log(ERR, MODULE) << "SplitReplicationPairForRevertSnapshot fail " << m_pairID << HCPENDLOG;
        return false;
    }
    return true;
}

bool ReplicationRestoreJob::RevertSnapShotInStorage()
{
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    std::vector<std::string> pairIdList;
    m_localDeviceController->QueryReplicationPairIds(m_basicFSID, pairIdList);
    SetSecondaryResourceProtect(pairIdList, false);
    AddLogDetail(logDetail, "nas_plugin_prepare_copy_data_label", JobLogLevel::TASK_LOG_INFO, m_snapshotName);
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::RUNNING);
    int64_t errorCode = MP_SUCCESS;
    if (!MonitorExistSnapshotRevert(errorCode)) {
        SetSecondaryResourceProtect(pairIdList, true);
        AddLogDetail(logDetail, "nas_plugin_prepare_copy_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, errorCode);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return false;
    }
    if (IsAbortJob()) {
        SetSecondaryResourceProtect(pairIdList, true);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::ABORTED);
        return false;
    }

    time_t revertStartTime = PluginUtils::GetCurrentTimeInSeconds();
    if (!m_nasReplicationServiceObj->RevertSnapShotInStorage(m_localDeviceController, m_snapshotName, m_snapshotId,
        errorCode)) {
        SetSecondaryResourceProtect(pairIdList, true);
        AddLogDetail(logDetail, "nas_plugin_prepare_copy_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, errorCode);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return false;
    }
    if (!MonitorSnapshotRevert(errorCode, revertStartTime)) {
        AddLogDetail(logDetail, "nas_plugin_prepare_copy_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, errorCode);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return false;
    }
    SetSecondaryResourceProtect(pairIdList, true);
    return true;
}

void ReplicationRestoreJob::CleanTask(AppProtect::JobResult::type status)
{
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    HCP_Log(INFO, MODULE) << "Start clean task..." << HCPENDLOG;
    // 公共：pair分裂，并进行主从切换
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
    if (!m_nasReplicationServiceObj->SplitReplicationPair(m_localDeviceController, m_pairID)) {
        HCP_Log(ERR, MODULE) << "Failed to split replication pair: " << m_pairID << HCPENDLOG;
        HCP_Log(ERR, MODULE) << "The error code of spliting replication pair is "
            << m_localDeviceController->GetErrorCode() << HCPENDLOG;
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
    }
    // pair创建完毕后，在本端检查当前pair主从关系是否满足要求，如果不满足则需要进行主从切换处理, 确保生产端为主
    ReplicationPairInfo replicationPairInfo;
    if (!m_nasReplicationServiceObj->GetPairInfoByID(m_remoteDeviceController, m_pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Get pair info fail：" << m_pairID << HCPENDLOG;
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
        return;
    }
    if (!replicationPairInfo.isPrimary) {
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
        if (!SwitchOverPrimaryAndSecondResource(m_remoteDeviceController, m_pairID)) {
            HCP_Log(ERR, MODULE) << "SwitchOverPrimaryAndSecondResource fail：" << m_pairID << HCPENDLOG;
            HCP_Log(ERR, MODULE) << "The error code of SwitchOverPrimaryAndSecondResource is "
                << m_remoteDeviceController->GetErrorCode() << HCPENDLOG;
            REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
        }
    }
    return;
}

bool ReplicationRestoreJob::MonitorSnapshotRevert(int64_t &errorCode, time_t revertStartTime)
{
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    int32_t previousTaskProgress = PROGRESS_TEN;
    std::string rollbackRate = "";
    std::string rollbackStatus = "0";
    long rollbackRateInt = MAX_ROLLBACK_RATE;
    int32_t taskProgress = previousTaskProgress;
    bool snapshotRollbackFlag = false; // 快照回滚使用endtime做判断的标识
    while (true) {
        if (IsAbortJob()) {
            return false;
        }
        sleep(SCAN_TIME_INTERVAL_SECEND);
        std::string endtime;
        HCP_Log(INFO, MODULE) << "Enter MonitorSnapshotRevert........" << HCPENDLOG;
        errorCode = m_nasReplicationServiceObj->QueryRevertEndInfoInStorage(m_localDeviceController,
            rollbackRate, rollbackStatus, endtime, m_snapshotId);
        HCP_Log(INFO, MODULE) << "Query rollbackRate：" << rollbackRate << " rollbackStatus: " << rollbackStatus <<
            " endtime: " << endtime << " snapshotId: " << m_snapshotId << HCPENDLOG;
        if (errorCode == MP_FAILED) {
            HCP_Log(ERR, MODULE) << "Query Revert Info RollbackRate Failed" << HCPENDLOG;
            return false;
        }
        if (rollbackRate != "") {
            rollbackRateInt = std::stol(rollbackRate);
        }
        rollbackRateInt = (rollbackRateInt >= MAX_ROLLBACK_RATE ? MAX_ROLLBACK_RATE : rollbackRateInt);
        if (previousTaskProgress <= SNAPSHOT_REVERT_FINISHED) {
            taskProgress = uint64_t((SNAPSHOT_REVERT_FINISHED - previousTaskProgress) *
                (rollbackRateInt * 1.0 / PROGRESS_OVERALL) + previousTaskProgress);
            m_curProcess = taskProgress;
            REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::RUNNING);
        }
        // rollbackStatus状态为0,说明本次回滚结束
        if (rollbackStatus == "0") {
            HCP_Log(INFO, MODULE) << "RevertStartTime："<< revertStartTime << " endtime:" << endtime << HCPENDLOG;
            snapshotRollbackFlag = true;
            break;
        }
    }
    if (snapshotRollbackFlag) {
        sleep(SCAN_TIME_INTERVAL_SECEND * MONITOR_ROLLBACK_TIMES); // 查询到endtime之后等待30s确保任务结束
    }
    m_curProcess = SNAPSHOT_REVERT_FINISHED;
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::RUNNING);
    return true;
}

// 设置A8000所属的Dorado的文件系统状态的pair是否保护
void ReplicationRestoreJob::SetSecondaryResourceProtect(const std::vector<std::string> pairIdList, bool isActive)
{
    for (std::size_t index = 0; index < pairIdList.size(); index++) {
        std::string pairID = pairIdList[index];
        ReplicationPairInfo replicationPairInfo;
        if (m_nasReplicationServiceObj->GetPairInfoByID(m_localDeviceController, pairID, replicationPairInfo)) {
            if (replicationPairInfo.isPrimary) {
                continue;
            }
            if (isActive) {
                m_nasReplicationServiceObj->SecondaryResourceProtectEnable(m_localDeviceController, pairID);
            } else {
                m_nasReplicationServiceObj->SecondaryResourceProtectDisable(m_localDeviceController, pairID);
            }
        }
    }
}

bool ReplicationRestoreJob::ConfirmRestoreLocation(RestoreLocation &restoreLocation)
{
    std::string targetEsn = "";
    std::string targetFileSystemID = "";
    std::string productEsn = "";

    m_nasReplicationServiceObj = NasRepServiceObjOp::NasReplicationService::GetReplicationInstance(
        m_productFSID, m_basicFSID, m_qos);
    m_nasReplicationServiceObj->SetTaskID(m_parentJobId);

    if (!m_nasReplicationServiceObj->GetPairInfoInProduct(
        m_remoteDeviceController, m_localDeviceController, m_pairID)) {
        HCP_Log(ERR, MODULE) << "Get pair id failed:" << HCPENDLOG;
        return false;
    }

    for (std::size_t index = 0; index < m_restorePara->copies[0].repositories.size(); index++) {
        StorageRepository storage = m_restorePara->copies[0].repositories[index];
        if (storage.repositoryType == RepositoryDataType::type::DATA_REPOSITORY) {
            ProductExtendInfo extendInfo {};
            if (!JsonHelper::JsonStringToStruct(storage.extendInfo, extendInfo)) {
                HCP_Log(ERR, MODULE) << "ProductExtendInfo is invaild." << HCPENDLOG;
                return false;
            }
            targetFileSystemID = extendInfo.fileSystemId;
            targetEsn = extendInfo.productEsn;
        }
    }
    if (targetEsn == "" || targetFileSystemID == "") {
        HCP_Log(ERR, MODULE) << "ESN is invaild." << HCPENDLOG;
        return false;
    }
    if (m_remoteDeviceController->GetESN(productEsn) != MP_SUCCESS) {
        return false;
    }
    if (productEsn != targetEsn) {
        restoreLocation =  RestoreLocation::NEWPOS_AND_DIFFFILESYTEM;
        HCP_Log(INFO, MODULE) << "Confirm:restore to a new device" << HCPENDLOG;
        return true;
    }
    if (m_productFSID == targetFileSystemID) {
        restoreLocation =  RestoreLocation::ORIGINAL_AND_SAMEFILESYTEM;
        HCP_Log(INFO, MODULE) << "Confirm:restore to original filesystem" << HCPENDLOG;
        return true;
    }
    restoreLocation =  RestoreLocation::ORIGINAL_AND_DIFFFILESYTEM;
    HCP_Log(INFO, MODULE) << "Confirm:restore to a new filesystem of the original device" << HCPENDLOG;
    return true;
}

bool ReplicationRestoreJob::DeletePairWhenRestore(const RestoreLocation &restoreLocation)
{
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;
    if (restoreLocation == RestoreLocation::ORIGINAL_AND_SAMEFILESYTEM ||
        restoreLocation == RestoreLocation::ORIGINAL_AND_DIFFFILESYTEM) {
        HCP_Log(INFO, MODULE) << "No need to delete the pair " << m_pairID << HCPENDLOG;
        return true;
    }
    HCP_Log(INFO, MODULE) << "Restore to a new device, need to delete the pair " << m_pairID << HCPENDLOG;
    AddLogDetail(logDetail, "nas_plugin_pair_split_label", JobLogLevel::TASK_LOG_INFO);
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
    if (!m_nasReplicationServiceObj->SplitReplicationPair(m_localDeviceController, m_pairID)) {
        HCP_Log(ERR, MODULE) << "SplitReplicationPair fail：" << m_pairID << HCPENDLOG;
        AddLogDetail(logDetail, "nas_plugin_pair_split_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, m_localDeviceController->GetErrorCode());
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::FAILED);
        return false;
    }
    HCP_Log(INFO, MODULE) << "Pair " << m_pairID  << " change to be invalid. Delete Pair." << HCPENDLOG;
    AddLogDetail(logDetail, "nas_plugin_pair_delete_label", JobLogLevel::TASK_LOG_INFO);
    REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::RUNNING);
    if (!m_nasReplicationServiceObj->DeleteReplicatonPair(m_localDeviceController, m_pairID)) {
        HCP_Log(ERR, MODULE) << "Delete ReplicationPair fail：" << m_pairID << HCPENDLOG;
        AddLogDetail(logDetail, "nas_plugin_pair_delete_fail_label", JobLogLevel::TASK_LOG_ERROR);
        if (IsReportAgentErrorCode(logDetail)) {
            return false;
        }
        AddErrCode(logDetail, m_localDeviceController->GetErrorCode());
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, 0, 0, SubJobStatus::FAILED);
        return false;
    }
    return true;
}

bool ReplicationRestoreJob::GetJobInfoBody()
{
    if (m_jobCommonInfo == nullptr) {
        HCP_Log(ERR, MODULE) << "jobCommonInfo is null" << HCPENDLOG;
        return false;
    }
    m_restorePara = std::dynamic_pointer_cast<AppProtect::RestoreJob>(GetJobInfo()->GetJobInfo());
    m_qos = m_restorePara->jobParam.qos.bandwidth;
    m_snapshotName = m_restorePara->copies[0].name;
    return true;
}
